import json
import os
import joblib
import numpy as np
import pandas as pd
from statistics import mode
from collections import Counter
from django.conf import settings
from django.http import JsonResponse, HttpResponse
from django.shortcuts import render
from django.views import View

file_name = ''
# Create predict views here.


# 上传测试集、保存测试集
class Upload(View):
    """预测：上传测试集"""

    def get(self, request):
        context = {
            'describe': '测试集',
        }
        return render(request, 'predict/predict-upload.html', context=context)

    def post(self, request):

        global file_name
        csv_file = request.FILES.get('csv_file')
        file_name = csv_file.name
        if file_name.split('.')[1] != 'csv':
            return JsonResponse({'code': 0})

        file_path = os.path.join(settings.MEDIA_ROOT, 'csv_dir\\' + file_name)

        # 学到
        with open(file_path, 'wb+') as f:
            for chunk in csv_file.chunks():
                f.write(chunk)

        return JsonResponse({'code': 1})


# 执行预测
def execute_predict(request):
    # 数据预处理
    # 读入 CSV 文件
    df = pd.read_csv(os.path.join(settings.MEDIA_ROOT, 'csv_dir\\' + file_name))
    df = df.apply(lambda x: x.fillna(x.mean()), axis=0)
    data = df.iloc[::, 1:108:]

    sample_size = len(data)  # 行，样本数量
    feature_quantity = len(data.columns)  # 列，特征数

    # 加载三种模型
    # lg = joblib.load(os.path.join(settings.STATICFILES_DIRS[0], 'model\\' + 'LightGBM'))
    # xg = joblib.load(os.path.join(settings.STATICFILES_DIRS[0], 'model\\' + 'XgBoost'))
    # cb = joblib.load(os.path.join(settings.STATICFILES_DIRS[0], 'model\\' + 'CatBoost'))
    # model = joblib.load(os.path.join(settings.STATICFILES_DIRS[0], 'model\\' + 'model_pick'))
    lg = joblib.load(os.path.join(settings.STATIC_ROOT, 'model\\' + 'LightGBM'))
    xg = joblib.load(os.path.join(settings.STATIC_ROOT, 'model\\' + 'XgBoost'))
    cb = joblib.load(os.path.join(settings.STATIC_ROOT, 'model\\' + 'CatBoost'))
    model = joblib.load(os.path.join(settings.STATIC_ROOT, 'model\\' + 'model_pick'))

    # 选择模型进行预测
    yp = our_model(data, m1=lg, m2=xg, m3=cb, model=model)
    counter = Counter(yp)

    # 输出分类结果（JSON格式）
    unique = []
    counts = []
    for k, v in counter.items():
        unique.append(k)
        counts.append(v)
    # 对输出结果排序
    unique.sort()
    # 生成统计每种分类的数量
    counts = [dict(counter)[k] for k in unique]
    # 转变为echarts可以接收的格式
    pie_data = dict(counter)
    # 将int64类型的数据转换为int类型
    df['sample_id'] = df['sample_id'].astype(int)
    # 提取sample_id和预测值列的数据
    sample_ids = df['sample_id'].tolist()
    predictions = yp
    # 创建字典
    output_dict = {str(sample_id): prediction for sample_id, prediction in zip(sample_ids, predictions)}
    # 指定保存JSON文件的路径和文件名
    output_file = 'output.json'
    # 将字典保存为JSON文件，指定ensure_ascii=False参数
    with open(os.path.join(settings.MEDIA_ROOT, 'result\\' + output_file), 'w') as f:
        json.dump(output_dict, f, ensure_ascii=False)

    # 返回结果给可视化界面
    context = {
        'unique': unique,
        'counts': counts,
        'pie_data': pie_data,
        'sample_size': sample_size,
        'feature_quantity': feature_quantity,
    }

    # 返回结果给可视化模版
    response = render(request, 'predict/predict-visualization.html', context=context)
    predict_count = int(request.COOKIES.get('predict'))
    predict_count += 1
    response.set_cookie('predict', predict_count)
    return response


# 我们自己的预测模型
def our_model(X_test, m1, m2, m3, model):
    i = 0
    t = []
    m = []
    yt = []
    yp = []
    l = []
    pred_l = []
    pro_l = []

    # For each class (normal or a type of attack), find the leader model
    for index, row in X_test.iterrows():
    # 将行转换为一维数组，并添加到列表中
        xi2 = row.values.reshape((107,))

        y_pred1 = m1.predict(xi2.reshape(1, -1))      # model 1 (LightGBM) makes a prediction on text sample xi
        y_pred1 = int(y_pred1[0])
        y_pred2 = m2.predict(xi2.reshape(1, -1),validate_features=False)      # model 2 (XGBoost) makes a prediction on text sample xi
        y_pred2 = int(y_pred2[0])
        y_pred3 = m3.predict(xi2.reshape(1, -1))      # model 3 (Catboost) makes a prediction on text sample xi
        y_pred3 = int(y_pred3[0])

        p1 = m1.predict_proba(xi2.reshape(1, -1))     # The prediction probability (confidence) list of model 1
        p2 = m2.predict_proba(xi2.reshape(1, -1))     # The prediction probability (confidence) list of model 2
        p3 = m3.predict_proba(xi2.reshape(1, -1))     # The prediction probability (confidence) list of model 3

        # Find the highest prediction probability among all classes for each ML model
        y_pred_p1 = np.max(p1)
        y_pred_p2 = np.max(p2)
        y_pred_p3 = np.max(p3)

        if y_pred1 == y_pred2 == y_pred3: # If the predicted classes of all the three models are the same
            y_pred = y_pred1 # Use this predicted class as the final predicted class

        elif y_pred1 != y_pred2 != y_pred3: # If the predicted classes of all the three models are different
            # For each prediction model, check if the predicted class’s original ML model is the same as its leader model
            if model[y_pred1]==m1: # If they are the same and the leading model is model 1 (LightGBM)
                l.append(m1)
                pred_l.append(y_pred1) # Save the predicted class
                pro_l.append(y_pred_p1) # Save the confidence

            if model[y_pred2]==m2: # If they are the same and the leading model is model 2 (XGBoost)
                l.append(m2)
                pred_l.append(y_pred2)
                pro_l.append(y_pred_p2)

            if model[y_pred3]==m3: # If they are the same and the leading model is model 3 (CatBoost)
                l.append(m3)
                pred_l.append(y_pred3)
                pro_l.append(y_pred_p3)

            if len(l)==0: # Avoid empty probability list
                pro_l=[y_pred_p1,y_pred_p2,y_pred_p3]

            elif len(l)==1: # If only one pair of the original model and the leader model for each predicted class is the same
                y_pred=pred_l[0] # Use the predicted class of the leader model as the final prediction class

            else: # If no pair or multiple pairs of the original prediction model and the leader model for each predicted class are the same
                max_p = max(pro_l) # Find the highest confidence

                # Use the predicted class with the highest confidence as the final prediction class
                if max_p == y_pred_p1:
                    y_pred = y_pred1
                elif max_p == y_pred_p2:
                    y_pred = y_pred2
                else:
                    y_pred = y_pred3

        else: # If two predicted classes are the same and the other one is different
            n = mode([y_pred1,y_pred2,y_pred3]) # Find the predicted class with the majority vote
            if model[n] == m2:
                y_pred = model[n].predict(xi2.reshape(1, -1),validate_features=False)
            else:
                y_pred = model[n].predict(xi2.reshape(1, -1)) # Use the predicted class of the leader model as the final prediction class
            y_pred = int(y_pred[0])

        yp.append(y_pred) # Save the predicted classes for all tested samples
    return yp


# 下载预测结果
def download(request):
    # 读取要下载的文件
    filepath = os.path.join(settings.MEDIA_ROOT, 'result\\' + 'output.json')

    with open(filepath, 'rb') as f:
        filedata = f.read()

    # 返回响应，弹出文件下载对话框
    response = HttpResponse(filedata, content_type='application/json')
    response['Content-Disposition'] = f'attachment; filename="output.json"'

    return response
